<?php

if( ! defined('DOCROOT') )
{
    require_once '/var/www/advancelab/chilik2/unittest/bootstrap.php';
}

require __DIR__.'/../source/constructor.php';

class ConstructorTest extends UnitTestCase
{
    protected $start;
    protected $mem;
    
    function setUp()
    {
        $this->start = microtime( TRUE );
        $this->mem = memory_get_usage();
    }
    
    function testingSetUp()
    {
        ob_start();
        
        $factory = new Constructor();
        $this->assertTrue( $factory instanceof Constructor );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingConstructorCtor()
    {
        $factory = new Constructor();
        
        $this->assertTrue( $factory instanceof Constructor );
        $this->assertIsA( $factory, 'Constructor' );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingConstructorCtorSuccess()
    {
        $factory = new Constructor();
        
        $this->assertTrue( $factory instanceof Constructor );
        $this->assertIsA( $factory, 'Constructor' );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingConstructorCtorFailure()
    {
        $test = array( TRUE, FALSE, 88, 88.99, 'test', '', array(), new stdClass() );
        
        foreach( $test as $val )
        {
            $this->expectError();
            $factory = new Constructor( $val );
            /*
            
            try
            {
                $factory = new Constructor( $val );
                $this->fail( 'Expected exception was not thrown' );
            }
            catch( \Exception $e )
            {
                $this->assertEqual( 'Constructor\BaseValueException', get_class( $e ) );
            }
            */
        }
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingConstructorHasSuccess()
    {
        $factory = new Constructor();
        $factory->set( new \stdClass() );
        
        dump( $factory );
        
        $this->assertTrue( $factory->has( 'stdClass' ) );
        $this->assertFalse( $factory->has( 'NonExistent' ) );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingConstructorHasFailure()
    {
        $test = array( NULL, TRUE, FALSE, 88, 88.99, array(), new stdClass() );
        
        $factory = new Constructor();
        
        foreach( $test as $val )
        {
            try
            {
                $factory->has( $val );
                $this->fail( 'Expected exception was not thrown' );
            }
            catch( \Exception $e )
            {
                $this->assertEqual( 'Constructor\BaseValueException', get_class( $e ) );
            }
        }
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingConstructorGetSuccess()
    {
        $factory = new Constructor();
        $factory->set( new \stdClass() );
        
        $this->assertEqual( new \stdClass, $factory->get( 'stdClass' ) );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingConstructorGetFailure()
    {
        $test = array( NULL, TRUE, FALSE, 88, 88.99, '', array(), new stdClass() );
        
        $factory = new Constructor();
        
        foreach( $test as $val )
        {
            try
            {
                $factory->get( $val );
                $this->fail( 'Expected exception was not thrown' );
            }
            catch( \Exception $e )
            {
                $this->assertEqual( 'Constructor\BaseValueException', get_class( $e ) );
            }
        }
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingConstructorSetSuccess()
    {
        $factory = new Constructor();
        $factory->set( new \stdClass() );
        
        $this->assertEqual( new \stdClass, $factory->get( 'stdClass' ) );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingConstructorSetFailure()
    {
        $test = array( NULL, TRUE, FALSE, 88, 88.99, '', array() );
        $test2 = array( NULL, TRUE, FALSE, 88, 88.99, '', array(), new stdClass() );
        
        $factory = new Constructor();
        
        foreach( $test as $val )
        {
            try
            {
                $factory->set( $val );
                $this->fail( 'Expected exception was not thrown' );
            }
            catch( \Exception $e )
            {
                $this->assertEqual( 'Constructor\BaseValueException', get_class( $e ) );
            }
            
            foreach( $test as $val2 )
            {
                try
                {
                    $factory->set( $val, $val2 );
                    $this->fail( 'Expected exception was not thrown' );
                }
                catch( \Exception $e )
                {
                    $this->assertEqual( 'Constructor\BaseValueException', get_class( $e ) );
                }
                
                try
                {
                    $factory->set( $val, $val2 );
                    $this->fail( 'Expected exception was not thrown' );
                }
                catch( \Exception $e )
                {
                    $this->assertEqual( 'Constructor\BaseValueException', get_class( $e ) );
                }
                
                try
                {
                    $factory->set( NULL, $val2 );
                    $this->fail( 'Expected exception was not thrown' );
                }
                catch( \Exception $e )
                {
                    $this->assertEqual( 'Constructor\BaseValueException', get_class( $e ) );
                }
            }
        }
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingConstructorRegisterAliasSuccess()
    {
        $factory = new Constructor();
        
        $factory->register( 'stdClass', 'stdClass' );
        $this->assertEqual( 'stdClass', $factory->getAlias( 'stdClass' ) );
        
        $factory->register( 'std', 'stdClass' );
        $this->assertEqual( 'stdClass', $factory->getAlias( 'std' ) );
        
        $factory->register( 'test', 'std' );
        $this->assertEqual( 'stdClass', $factory->getAlias( 'test' ) );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingConstructorRegisterAliasFailure()
    {
        $test = array( NULL, TRUE, FALSE, 88, 88.99, '', array(), new stdClass() );
        
        $factory = new Constructor();
        
        foreach( $test as $val )
        {
            try
            {
                $factory->register( $val );
                $this->fail( 'Expected exception was not thrown' );
            }
            catch( \Exception $e )
            {
                $this->assertEqual( 'Constructor\BaseValueException', get_class( $e ) );
            }
            
            try
            {
                $factory->register( $val, NULL );
                $this->fail( 'Expected exception was not thrown' );
            }
            catch( \Exception $e )
            {
                $this->assertEqual( 'Constructor\BaseValueException', get_class( $e ) );
            }
            
            try
            {
                $factory->register( $val, $val );
                $this->fail( 'Expected exception was not thrown' );
            }
            catch( \Exception $e )
            {
                $this->assertEqual( 'Constructor\BaseValueException', get_class( $e ) );
            }
            
            try
            {
                $factory->register( NULL, $val );
                $this->fail( 'Expected exception was not thrown' );
            }
            catch( \Exception $e )
            {
                $this->assertEqual( 'Constructor\BaseValueException', get_class( $e ) );
            }
            
        }
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingConstructorGetAliasSuccess()
    {
        $factory = new Constructor();
        
        $factory->register( 'stdClass', 'stdClass' );
        $this->assertEqual( 'stdClass', $factory->getAlias( 'stdClass' ) );
        
        $factory->register( 'std', 'stdClass' );
        $this->assertEqual( 'stdClass', $factory->getAlias( 'std' ) );
        
        $factory->register( 'test', 'std' );
        $this->assertEqual( 'stdClass', $factory->getAlias( 'test' ) );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingConstructorGetAliasFailure()
    {
        $test = array( NULL, TRUE, FALSE, 88, 88.99, '', array(), new stdClass() );
        $factory = new Constructor();
        
        foreach( $test as $val )
        {
            try
            {
                $factory->getAlias( $val );
                $this->fail( 'Expected exception was not thrown' );
            }
            catch( \Exception $e )
            {
                $this->assertEqual( 'Constructor\BaseValueException', get_class( $e ) );
            }
        }
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingConstructorCreateSuccess()
    {
        $factory = new Constructor();
        $object = $factory->create( 'NoDependencies' );
        
        $this->assertEqual( new NoDependencies, $object );
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingConstructorCreateFailure()
    {
        $test = array( TRUE, FALSE, 88, 88.99, array(), new \stdClass()  );
        
        $factory = new Constructor();
        
        foreach( $test as $val )
        {
            try
            {
                $factory->create( $val );
                $this->fail( 'Expected exception was not thrown' );
            }
            catch( \Exception $e )
            {
                $this->assertEqual( 'Constructor\BaseValueException', get_class( $e ) );
            }
        }
        
        try
        {
            $factory->create( 'NonExistentClass' );
            $this->fail( 'Expected exception was not thrown' );
        }
        catch( \Exception $e )
        {
            $this->assertEqual( 'Dispatcher\Exception', get_class( $e ) );
        }
    
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingConstructorRegisterWithOtherClassSuccess()
    {
        $factory = new Constructor();
        $factory->register( 'NoDependencies', 'ExtendsNoDependencies' );
        
        $this->assertEqual( new ExtendsNoDependencies, $factory->get( 'NoDependencies' ) );
        $this->assertNotEqual( new NoDependencies, $factory->get( 'NoDependencies' ) );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingConstructorRegisterWithOtherClassFailure()
    {
        $test = array( NULL, TRUE, FALSE, 88, 88.99, '', array(), new \stdClass()  );
        $factory = new Constructor();
        
        foreach( $test as $val )
        {
            try
            {
                $factory->register( $val );
                $this->fail( 'Expected exception was not thrown' );
            }
            catch( \Exception $e )
            {
                $this->assertEqual( 'Constructor\BaseValueException', get_class( $e ) );
            }
            
            foreach( $test as $val2 )
            {
                try
                {
                    $factory->register( $val, $val2 );
                    $this->fail( 'Expected exception was not thrown' );
                }
                catch( \Exception $e )
                {
                    $this->assertEqual( 'Constructor\BaseValueException', get_class( $e ) );
                } 
            }
        }
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    
    function testingConstructorUndefinedClass()
    {
        $factory = new Constructor();
        
        try
        {
            $factory->create( 'RequireUndefinedClass' );
            $this->fail( 'Expected exception was not thrown.' );
        }
        catch( \Exception $e )
        {
            $this->assertEqual( 'Class ClassThatDoesNotExist does not exist', $e->getMessage() );
        }
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingCreateEmptyContainer()
    {
        $factory = new Constructor();
        
        $this->assertEqual( 'Constructor', get_class( $factory ) );
        $this->assertIsA( $factory, 'Constructor' );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingCreateContainerWithNoDependencies()
    {
        $factory = new Constructor();
        $result = $factory->create( 'NoDependencies' );
        
        $this->assertIsA( $result, 'NoDependencies' );
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingCreateContainerWithClassDependencies()
    {
        $factory = new Constructor();
        $result = $factory->create( 'SingleClassDependency' );
        
        $this->assertIsA( $result, 'SingleClassDependency' );
        $this->assertIsA( $result->val, 'NoDependencies' );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingCreateContainerWithDefaultValue()
    {
        $factory = new Constructor();
        $result = $factory->create( 'DefaultValue' );
        
        $this->assertIsA( $result, 'DefaultValue' );
        $this->assertEqual( $result->val, 88 );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingCreateContainerWithNoSpecifiedTypeDependenciesNowSupported()
    {
        $factory = new Constructor();
        $result = $factory->create( 'NoSpecifiedTypeDependencies' );
        
        $this->assertEqual( new NoSpecifiedTypeDependencies( NULL ), $result );
        
        /*
        before: not supported with variable
        try
        {
            $result = $factory->create( 'NoSpecifiedTypeDependencies' );
            $this->fail( 'Expected exception was not thrown.' );
        }
        catch( \Exception $e )
        {
            $this->assertEqual( 'Constructor\FactoryParameterFailureException', get_class( $e ) );
        }
        */
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingCreateContainerWithRegisteredInterfaceFailure()
    {
        $factory = new Constructor();
        
        try
        {
            $result = $factory->create( 'DependsOnInterface' );
            $this->fail( 'Expected exception was not thrown.' );
        }
        catch( \Exception $e )
        {
            $this->assertEqual( 'Constructor\FactoryParameterFailureException', get_class( $e ) );
        }
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingCreateContainerWithRegisteredInterfaceSuccess()
    {
        $factory = new Constructor();
        
        $factory->register( 'AnInterface', 'ConcreteImplementation' );
        $result = $factory->create( 'DependsOnInterface' );
        
        $this->assertIsA( $result, 'DependsOnInterface' );
        $this->assertIsA( $result->val, 'ConcreteImplementation' );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingCreateContainerWithDifferentImplementationSuccess()
    {
        $factory = new Constructor();
        
        $factory->register( 'NoDependencies', 'ExtendsNoDependencies' );
        $result = $factory->create( 'NoDependencies' );
        
        $this->assertTrue( $result instanceof ExtendsNoDependencies );
        $this->assertTrue( $result instanceof NoDependencies );
        
        $result = $factory->create( 'SingleClassDependency' );
        $this->assertIsA( $result, 'SingleClassDependency' );
        $this->assertIsA( $result->val, 'ExtendsNoDependencies' );
        
        $this->assertTrue( $result instanceof SingleClassDependency );
        $this->assertTrue( $result->val instanceof ExtendsNoDependencies );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingCreateContainerWithGetSuccess()
    {
        $factory = new Constructor();
        $result = $factory->get( 'NoDependencies' );
        
        $this->assertIsA( $result, 'NoDependencies' );
        $this->assertTrue( $result instanceof NoDependencies );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingCreateContainerWithGeSameReferencetSuccess()
    {
        $factory = new Constructor();
        
        $result = $factory->get( 'NoDependencies' );
        $result2 = $factory->get( 'NoDependencies' );
        
        $this->assertIsA( $result, 'NoDependencies' );
        $this->assertTrue( $result instanceof NoDependencies );
        $this->assertIsA( $result2, 'NoDependencies' );
        $this->assertTrue( $result2 instanceof NoDependencies );
        
        $this->assertEqual( $result, $result2 );
        $this->assertReference( $result, $result2 );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingBucketReturnSameInstance()
    {
        $factory = new Constructor();
        $object = new \StdClass();
        
        $factory->set( $object );
        
        $this->assertTrue( $factory instanceof Constructor );
        $this->assertIsA( $object, 'stdClass' );
        $this->assertEqual( $object, $factory->get( 'stdClass' ) );
        $this->assertReference( $object, $factory->get( 'stdClass' ) );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingBucketReturnDifferentInstanceDueToCaseSensitive()
    {
        $factory = new Constructor();
        $object = new \StdClass();
        
        $factory->set( $object );
        
        $this->assertTrue( $factory instanceof Constructor );
        $this->assertIsA( $object, 'stdClass' );
        $this->assertEqual( $object, $factory->get( 'StdClass' ) );
        $this->assertNotReference( $object, $factory->get( 'StdClass' ) );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingConstructorWithAlias()
    {
        $factory = new Constructor();
        
        $factory->register( 'testWithNamespace', 'Test\WithNamespace' );
        $test = $factory->get( 'testWithNamespace' );
        
        $this->assertNotNull( $test );
        $this->assertEqual( new Test\WithNamespace, $test );
        
        $factory->register( 'testNamespace', 'testWithNamespace' );
        $test2 = $factory->get( 'testNamespace' );
        
        $this->assertNotNull( $test2 );
        $this->assertEqual( new Test\WithNamespace, $test2 );
        $this->assertReference( $test, $test2 );
        
        $factory->register( 'test', 'testNamespace' );
        $test3 = $factory->get( 'test' );
        
        $this->assertNotNull( $test3 );
        $this->assertEqual( new Test\WithNamespace, $test3 );
        
        $this->assertReference( $test, $test3 );
        $this->assertReference( $test2, $test3 );
    }
    
    function testingConstructorRegisterSuccess()
    {
        $factory = new Constructor();
        $factory->register( 'test', 'NoDependencies' );
        
        $this->assertEqual( $factory->get( 'test' ), $factory->get( 'NoDependencies' )  );
        $this->assertReference( $factory->get( 'test' ), $factory->get( 'NoDependencies' )  );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
    }
    
    function testingConstructorCreateItself()
    {
        $factory = new Constructor();
        
        $this->assertNotNull( $factory->get( 'Constructor' ) );
        $this->assertNotNull( $factory->get( 'classwithvar' ) );
        $this->assertNotNull( $factory->get( 'ClassWithVarArray' ) );
        
        $this->dump( __METHOD__.' >> Elapsed: '.round( ( microtime( 'time' ) - $this->start ), 3).' Memory Used: '.( (memory_get_usage() - $this->mem )/ 1024 ).' KB' );
        
        //ob_end_clean();
    }
       
}
